﻿using System.Collections.Generic;
using CodePlex.SharePointInstaller.Commands;
using CodePlex.SharePointInstaller.Configuration;
using CodePlex.SharePointInstaller.Wrappers;
using Microsoft.SharePoint.Administration;
using Rhino.Mocks;

namespace CodePlex.SharePointInstaller.Tests
{
    #region MSTest/NUnit Compatability

    // Define MSTEST as a conditional compilation symbol in your DEBUG build then you can do testing with your DEBUG test builds
    // directly in Visual Studio. This also means you can debug your tests.
    // Note, if you use any MSTest specific functionality that is NOT available in NUnit, it WILL NOT WORK in NUnit!
    // Such functionality includes any direct use of TestContext, any web service related attributes and other stuff I haven’t yet dug up but know exists!
    // Any NUnit functionality WILL work in MSTest since the NUnit Assert is THE Assert class and the NUnit.Framework assembly
    // is referenced and used. Any test assemblies that have been built with MSTEST specified will NOT work in NUnit, therefore
    // it is suggested that from time to time you create a Release build of your tests. If you have used any MSTest only functionality
    // then your build (and consequently the build of your test on the build server) will FAIL. Check it!
    // Note, ensure your test is in DEBUG build before using VS to create unit tests or it will add MSTest usings into your Release build!

#if !MSTEST
#if DEBUG
#error ******* DEBUG builds should define MSTEST as a Conditional Compilation Symbol in Project Properties->Build Tab ************
#else
#warning ******* Test is compiled for NUnit ************
#endif
    using NUnit.Framework;
    using NUnit.Framework.Constraints;

    // The following using declarations allow the Visual Studio autogenerated framework to actually use NUnit attributes.
    // This means that where in the test code below we see the VS generated [TestClass()] attribute, it is “really” [TestFixture],
    // i.e. NUnit style with MSTest syntax – which gets autogenerated and is thus easier to “write” as VS does it for you.  

    using TestClass = NUnit.Framework.TestFixtureAttribute;
    using TestMethod = NUnit.Framework.TestAttribute;
    using TestCleanup = NUnit.Framework.TearDownAttribute;
    using TestInitialize = NUnit.Framework.SetUpAttribute;
    using ClassCleanup = NUnit.Framework.TestFixtureTearDownAttribute;
    using ClassInitialize = NUnit.Framework.TestFixtureSetUpAttribute;
    using TestContext = System.Object;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting.Web;
using Category = Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute;
#endif

    // Since NUnit’s Assert is a superset of MSTest’s Assert, we’ll treat Assert as the NUnit version and get the extra goodies.
    using Assert = NUnit.Framework.Assert;

    #endregion

    /// <summary>
    /// Unit test for the placeholders resolution algorithm
    /// </summary>
    [TestClass]
    public class ExecuteCustomCommandsTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get { return testContextInstance; }
            set { testContextInstance = value; }
        }


        private MockRepository mocks;
        private IContext context;

        [TestInitialize]
        public void Setup()
        {
            mocks = new MockRepository();
            var config = mocks.DynamicMock<InstallConfiguration>();
            var solution = mocks.DynamicMock<SolutionInfo>();
            context = mocks.DynamicMock<IContext>();
            SetupResult.For(context.SolutionInfo).Return(solution);
            SetupResult.For(context.Action).Return(InstallAction.Install);
            SetupResult.For(context.Configuration).Return(config);
        }

        #region Additional test attributes

        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //

        #endregion


        [TestMethod]
        public void TestNoTokens()
        {
            var siteCollection = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(siteCollection.Url).Return("http://server/path/rootweb");
            var site1 = mocks.DynamicMock<IEntityInfo>();
            var site2 = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(site1.Url).Return("http://server/path/rootweb/webone");
            SetupResult.For(site2.Url).Return("http://server/path/rootweb/webtwo");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo> { siteCollection });
            SetupResult.For(context.Sites).Return(new List<IEntityInfo> { site1, site2 });

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("http://literal/here"),
                                       new List<string> { "http://literal/here" }));
        }

        [TestMethod]
        public void TestWebTokenForSite()
        {
            var siteCollection = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(siteCollection.Url).Return("http://server/path/rootweb");
            var site = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(site.Url).Return("http://server/path/rootweb/subweb");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo> {siteCollection});
            SetupResult.For(context.Sites).Return(new List<IEntityInfo> {site});

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("{WebURL}/lists/somelist"),
                                       new List<string> { "http://server/path/rootweb/subweb/lists/somelist" }));
        }

        [TestMethod]
        public void TestWebTokenForSiteOnly()
        {
            var siteCollection = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(siteCollection.Url).Return("http://server/path/rootweb");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo> { siteCollection });
            SetupResult.For(context.Sites).Return(new List<IEntityInfo>());

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("{WebURL}/lists/somelist"),
                                       new List<string> { "http://server/path/rootweb/lists/somelist" }));
        }

        [TestMethod]
        public void TestWebTokenForWeb()
        {
            var site = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(site.Url).Return("http://server/path/rootweb/subweb");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo>());
            SetupResult.For(context.Sites).Return(new List<IEntityInfo> {site});

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("{WebURL}/lists/somelist"),
                                       new List<string> {"http://server/path/rootweb/subweb/lists/somelist"}));
        }

        [TestMethod]
        public void TestWebTokenForNone()
        {
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo>());
            SetupResult.For(context.Sites).Return(new List<IEntityInfo>());

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("{WebURL}/lists/somelist"),
                                       new List<string> {"{WebURL}/lists/somelist"}));
        }


        [TestMethod]
        public void TestSiteTokenForSite()
        {
            var siteCollection = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(siteCollection.Url).Return("http://server/path/rootweb");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo> {siteCollection});
            SetupResult.For(context.Sites).Return(new List<IEntityInfo>());

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("{SiteURL}/lists/somelist"),
                                       new List<string> {"http://server/path/rootweb/lists/somelist"}));
        }

        [TestMethod]
        public void TestSiteTokenForWeb()
        {
            var site = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(site.Url).Return("http://server/path/rootweb/subweb");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo>());
            SetupResult.For(context.Sites).Return(new List<IEntityInfo> {site});

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("{SiteURL}/lists/somelist"),
                                       new List<string> { "{SiteURL}/lists/somelist" }));
        }

        [TestMethod]
        public void TestSiteTokenForNone()
        {
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo>());
            SetupResult.For(context.Sites).Return(new List<IEntityInfo>());

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("{SiteURL}/lists/somelist"),
                                       new List<string> {"{SiteURL}/lists/somelist"}));
        }


        [TestMethod]
        public void TestMultipleTokens()
        {
            var site = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(site.Url).Return("http://server/path/rootweb/subweb");
            var siteCollection = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(siteCollection.Url).Return("http://server/path/rootweb");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo> {siteCollection});
            SetupResult.For(context.Sites).Return(new List<IEntityInfo> {site});

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("-param1 {WebURL}/lists/somelist -param2 {SiteURL}/page"),
                                       new List<string>
                                           {
                                               "-param1 http://server/path/rootweb/subweb/lists/somelist -param2 http://server/path/rootweb/page"
                                           }));
        }


        [TestMethod]
        public void TestMultipleTokensForMultiplesTargets()
        {
            var site1 = mocks.DynamicMock<IEntityInfo>();
            var site2 = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(site1.Url).Return("http://server/path/rootweb/webone");
            SetupResult.For(site2.Url).Return("http://server/path/rootweb/webtwo");
            var siteCollection = mocks.DynamicMock<IEntityInfo>();
            SetupResult.For(siteCollection.Url).Return("http://server/path/rootweb");
            SetupResult.For(context.WebApps).Return(new List<SPWebApplication>());
            SetupResult.For(context.SiteCollections).Return(new List<IEntityInfo> {siteCollection});
            SetupResult.For(context.Sites).Return(new List<IEntityInfo> {site1, site2});

            mocks.ReplayAll();
            var command = new ExecuteCustomCommands(context);
            Assert.IsTrue(CompareLists(command.ReplaceTokens("-param1 {WebURL}/lists/somelist -param2 {SiteURL}/page"),
                                       new List<string>
                                           {
                                               "-param1 http://server/path/rootweb/webone/lists/somelist -param2 http://server/path/rootweb/page",
                                               "-param1 http://server/path/rootweb/webtwo/lists/somelist -param2 http://server/path/rootweb/page"
                                           }));
        }


        private static bool CompareLists<T>(IList<T> list1, IList<T> list2)
        {
            if (list1.Count != list2.Count)
                return false;

            for (int i = 0; i < list1.Count - 1; i++)
            {
                if (!list1[i].Equals(list2[i]))
                    return false;
            }

            return true;
        }
    }
}